""""判别手势"""
import time

import data_struct
import tools
import math
import data
import numpy as np
from pynput.keyboard import Key, Controller
import autopy


keyboard = Controller()


def dist(landmark1, WRIST):
    """
    计算landmark1到腕部距离
    :param Standard_dist: 中指指尖到腕部距离，防止因手掌大小以及距离远近产生的误差
    :param landmark1:非腕部坐标
    :param WRIST:腕部坐标，作为坐标原点
    :return: landmark1到腕部距离
    """
    dist = math.sqrt(math.pow(WRIST.x - landmark1.x, 2) + math.pow(WRIST.y - landmark1.y, 2)) / Standard_dist
    return dist


"""启动手势"""

def start_1shot(hds_lm):
    """
    判断一帧是否符合启动模式
    :param hds_lm:
    """
    loss = 0  # 和标准启动手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    # print("current_dist长度", len(current_dist))  # 0
    for i in range(21):
        loss = loss + abs(current_dist[i] - data.START_GESTURE_STD[i])
    print("和标准启动手势的误差为", loss)
    if loss < data.LOSS_START:
        return True
    else:
        return False


def start_distinguish(hds_lm):
    if start_1shot(hds_lm):  # 当前帧是启动手势
        data.flag[0] += 1
    else:  # 当前帧不是启动手势,启动位归0
        data.flag[0] = 0
    if data.flag[0] >= data.CONTINUE_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        print("启动")


"""左右挥动手势"""
def Wave2Left(hds_lm):
    """
    判断向左挥动手势
    :param hds_lm: 单手坐标hds_lm[1][21]
    :return: true:向左挥动
    """
    Current_Landmark = hds_lm[0]  # 当前帧手坐标
    sign = 0  # 标志，如果sign仍为0，说明符合翻页手势
    for i in range(21):
        if Current_Landmark[i].x < data.memory_landmark[i].x:
            sign = 1
            break
    if sign == 0:  # 当前帧符合翻页
        # 当前帧符合翻页，在data.flag中记录+1
        data.flag[1] += 1
        # 计算当前帧相对orgin_landmark移动的距离(手腕移动距离）
        dist = Current_Landmark[0].x - data.origin_landmark[0].x
        if dist > data.MOV_DIST_THRESHOLD:
            print("向左挥动判定成功")
            Clear()
            return True
    else:  # 当前帧不符合翻页，说明有折返现象（向左了）
        data.flag[1] = 0  # 不符合翻页手势，翻页标志位清零
        """更新动态手势初始位置"""
        # data.origin_landmark = Current_Landmark  # 更改动态手势初始位置
    return False


def Wave2Right(hds_lm):
    """
    判断向右挥动手势：向右移动距离大于阈值即可
    :param hds_lm: 单手坐标hds_lm[1][21]
    :return: true:向右挥动
    """
    Current_Landmark = hds_lm[0]  # 当前帧手坐标
    sign = 0  # 标志，如果sign仍为0，说明符合翻页手势
    for i in range(21):
        if Current_Landmark[i].x > data.memory_landmark[i].x:  # 区别1
            sign = 1
            break
    if sign == 0:  # 当前帧符合翻页
        # print("当前帧符合翻页")
        data.flag[2] += 1
        # 计算当前帧相对orgin_landmark移动的距离(手腕移动距离）
        dist = data.origin_landmark[0].x - Current_Landmark[0].x  # 区别2
        if dist > data.MOV_DIST_THRESHOLD:
            print("向右挥动判定成功")
            Clear()
            return True
    else:  # 当前帧不符合翻页，说明有折返现象（向左了）
        data.flag[2] = 0  # 不符合翻页手势，翻页标志位清零
        """更新动态手势初始位置"""
        # data.origin_landmark = Current_Landmark  # 更改动态手势初始位置
    return False


"""握拳手势"""
def Fist_1shot(hds_lm):
    """
    判断一帧是否符合握拳手势
    :param hds_lm:
    """
    loss = 0  # 和标准启动手势的误差
    current_dist = tools.get_dist(hds_lm, 1)  # 当前检测到的一只手上所有点到腕部距离
    for i in range(21):
        loss = loss + abs(current_dist[i] - data.FIST_GESTURE_STD[i])
    print("和标准握拳手势的误差为", loss)
    if loss < data.LOSS_START:
        return True
    else:
        return False


def Fist_Distinguish(hds_lm):
    if Fist_1shot(hds_lm):  # 当前帧是启动手势
        data.flag[3] += 1
    else:  # 当前帧不是握拳手势,启动位归0
        data.flag[3] = 0
    if data.flag[3] >= data.CONTINUE_THRESHOLD:  # 连续启动帧达到20帧
        # 握拳判定成功
        print("握拳判定成功")
        Clear()
        return True
    return False

"""归零"""
def Clear():
    """判定成功一个手势后触发，将所有data中的记录信息清零"""
    data.flag = flag = [0, 0, 0, 0,0,0]
    data.memory_landmark = []
    data.origin_landmark = []
    data.origin_sign = 0

"""荧光笔"""
def mouse_1shot(hds_lm):
    """
        判断一帧是否符合鼠标模式
        :param hds_lm:
        """
    loss = 0  # 和标准启动手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    #print("current_dist：",current_dist)
    #print("current_dist长度", len(current_dist))  # 0

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.MOUSE_GESTURE_STD[i])
    #print("和标准鼠标手势的误差为", loss)
    if loss < data.LOSS_MOUSE:
        return True
    else:
        return False

def mouse_distinguish(hds_lm):
    """
    荧光笔
    :param hds_lm:
    """
    if mouse_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.flag[4] += 1
    else:  # 当前帧不是启动手势,启动位归0
        data.flag[4] = 0
    if data.flag[4] >= data.CONTINUE_MOUSE_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        print("进入mouse模式")

"""橡皮"""
def Rubber_1shot(hds_lm):
    """
        判断一帧是否符合橡皮模式
        :param hds_lm:
        """
    loss = 0  # 和标准橡皮手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    print("current_dist：",current_dist)

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.RUBBER_GESTURE_STD[i])
    print("和标准橡皮手势的误差为", loss)
    if loss < data.LOSS_RUBBER:
        return True
    else:
        return False
def Rubber_distinguish(hds_lm):
    """
    橡皮
    只负责在合理的条件下按下组合键
    :param hds_lm:
    """
    """如果已经进入橡皮模式"""
    if data.MOUSE_FLAG[2] >= data.CONTINUE_RUBBER_THRESHOLD:
        return

    if Rubber_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.MOUSE_FLAG[2] += 1
    else:  # 当前帧不是启动手势,启动位归0
        data.MOUSE_FLAG[2] = 0
    if data.MOUSE_FLAG[2] >= data.CONTINUE_RUBBER_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        #print("进入mouse模式")
        Com_Key("Rubber")#按下荧光笔组合键

def mouse_distinguish(hds_lm):
    """
    荧光笔
    :param hds_lm:
    """
    if mouse_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.flag[4] += 1
    else:  # 当前帧不是启动手势,启动位归0
        data.flag[4] = 0
    if data.flag[4] >= data.CONTINUE_MOUSE_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        print("进入mouse模式")


"""食指和中指竖起1帧判别函数"""
def click_1shot(hds_lm):
    """
        判断一帧是否符合点击模式
        :param hds_lm:
        """
    loss = 0  # 和标准click手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    #print("current_dist：",current_dist)
    # print("current_dist长度", len(current_dist))  # 0

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.CLICK_GESTURE_STD[i])
    print("和标准click手势的误差为", loss)
    if loss < data.LOSS_CLICK:
        return True
    else:
        return False

def Finger_inBox(hds_lm):
    x=hds_lm[0][8].x*data.wCam
    y=hds_lm[0][8].y*data.hCam
    if x>=data.pt1[0] and x<=data.pt2[0] and y>=data.pt1[1] and y<=data.pt2[1]:
        return True
    else:
        return False

"""组合键"""
def Com_Key(type):
    """
    组合键
    :param type: 笔的类型
    """
    if type=="Fluorescent_Pen":
        keyboard.press(Key.ctrl)
        keyboard.press('i')
        keyboard.release(Key.ctrl)
        keyboard.release('i')

        #time.sleep(2)#
    if type=="Rubber":
        keyboard.press(Key.ctrl)
        keyboard.press('e')
        keyboard.release(Key.ctrl)
        keyboard.release('e')
    if type=="mouse":
        keyboard.press(Key.ctrl)
        keyboard.press('a')
        keyboard.release(Key.ctrl)
        keyboard.release('a')
    if type=="Red_Pen":
        keyboard.press(Key.ctrl)
        keyboard.press('p')
        keyboard.release(Key.ctrl)
        keyboard.release('p')
    if type=="Clear_All":
        keyboard.press('e')
        keyboard.release('e')

"""控制ppt函数"""

def gesture(hds_lm):
    """
    监测到手就进入这个函数(不支持动态手势通法）
    :rtype: object
    :param hds_lm: 单手坐标,n[1][21],n中是坐标对象
    """
    # #0:获得当前帧坐标
    # data.current_landmark=hds_lm[0]

    """是否进入手掌命令模式判别"""
    if data.flag[0] < data.CONTINUE_THRESHOLD:
        start_distinguish(hds_lm)

    """手掌命令模式"""
    if data.flag[0] >= data.CONTINUE_THRESHOLD:  # 已处于启动状态

        """记录动态手势起始位置"""
        print("当前已处于启动状态")
        if data.origin_sign == 0:
            data.origin_landmark = hds_lm[0]
            data.origin_sign = 1  # 下次就不会重新记录了

        """2：判断是否向左挥动"""
        if Wave2Left(hds_lm):
            """键盘->键"""
            keyboard.press(Key.left)
            time.sleep(data.SLEEP_TIME)
            return

        """3:判断是否向右挥动"""
        if Wave2Right(hds_lm):
            """键盘<-键"""
            keyboard.press(Key.right)
            time.sleep(data.SLEEP_TIME)
            return
        """判断是否是握拳手势"""
        if Fist_Distinguish(hds_lm):
            keyboard.press(Key.esc)
            time.sleep(data.SLEEP_TIME)
            return

    """判断是否进入荧光笔模式"""
    Mouse(hds_lm)
    # 3:更新当前帧至data.memory_landmark
    data.memory_landmark = hds_lm[0]

"""荧光笔模式判别和执行函数"""
def Fluorescent_Pen(hds_lm):
    """
    荧光笔模式判别和执行函数
    :param hds_lm: 手[1][21]
    """
    # 1.1食指向上10帧,并且需要食指指尖在框内
    if data.flag[4] < data.CONTINUE_MOUSE_THRESHOLD:
        mouse_distinguish(hds_lm)
    else:
        """已进入荧光笔模式"""
        # 1:第一次进入鼠标模式打开荧光笔
        if data.FIRST_MOUSE:
            Com_Key("Fluorescent_Pen")
            data.FIRST_MOUSE = 0

        # print("已进入鼠标模式")
        """"""
        # 2:计算当前帧食指尖所在位置,x1 y1是摄像头坐标系
        x1 = hds_lm[0][8].x * data.wCam
        y1 = hds_lm[0][8].y * data.hCam

        # 3:判断几个手指竖起
        """如果食指竖起（荧光笔模式），先处理荧光笔位移"""
        if mouse_1shot(hds_lm) or click_1shot(hds_lm):
            # 3.1坐标系转换：camera窗口->屏幕，方法：线性插值

            """x3 y3是屏幕坐标系"""
            x3 = (x1 - data.pt1[0]) * data.wScr / data.wPt
            y3 = (y1 - data.pt1[1]) * data.hScr / data.hPt

            """平滑"""
            # （7）平滑，使手指在移动鼠标时，鼠标箭头不会一直晃动
            """摄像头坐标系"""
            pLocx = data.memory_landmark[8].x * data.wCam
            pLocy = data.memory_landmark[8].y * data.hCam
            """屏幕坐标系"""
            pLocx = (pLocx - data.pt1[0]) * data.wScr / data.wPt
            pLocy = (pLocy - data.pt1[1]) * data.hScr / data.hPt

            cLocx = pLocx + (x3 - pLocx) / data.smooth  # 当前的鼠标所在位置坐标
            cLocy = pLocy + (y3 - pLocy) / data.smooth

            # （8）移动鼠标,越界不移动
            if cLocx < data.wScr and cLocx > 0 and cLocy < data.hScr and cLocy > 0:
                print("clotx=", cLocx, "cloty", cLocy)
                autopy.mouse.move(cLocx, cLocy)  # 给出鼠标移动位置坐标

        if click_1shot(hds_lm):
            if data.HOLD_FLAG == 0:
                data.HOLD_FLAG = 1
                autopy.mouse.toggle(button=autopy.mouse.Button.LEFT, down=True)  # 松开左键
        else:
            if data.HOLD_FLAG == 1:
                data.HOLD_FLAG = 0
                autopy.mouse.toggle(button=autopy.mouse.Button.LEFT, down=False)  # 松开左键

        """橡皮模式"""

        """关闭鼠标模式"""
        """若重新检测到手掌启动手势,则关闭荧光笔模式，清空flag[4]"""
        if start_1shot(hds_lm):
            data.flag[4] = 0
            data.HOLD_FLAG = 0
            data.FIRST_MOUSE = 1
            Com_Key("Fluorescent_Pen")

"""荧光笔"""
def Fluorescent_Pen_1shot(hds_lm):
    """
        判断一帧是否符合鼠标模式
        :param hds_lm:
        """
    loss = 0  # 和标准荧光笔手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    print("current_dist：",current_dist)
    # print("current_dist长度", len(current_dist))  # 0

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.FUORESCENT_PEN_GESTURE_STD[i])
    print("和标准荧光笔手势的误差为", loss)
    if loss < data.LOSS_FUORESCENT_PEN:
        return True
    else:
        return False

def Fluorescent_Pen_distinguish(hds_lm):
    """
    荧光笔
    只负责在合理的条件下按下组合键
    :param hds_lm:
    """
    """如果已经进入鼠标模式"""
    if data.MOUSE_FLAG[0] >= data.CONTINUE_FUORESCENT_PEN_THRESHOLD:
        return

    if Fluorescent_Pen_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.MOUSE_FLAG[0] += 1
    else:  # 当前帧不是荧光手势,荧光笔位归0
        data.MOUSE_FLAG[0] = 0
    if data.MOUSE_FLAG[0] >= data.CONTINUE_FUORESCENT_PEN_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        print("进入mouse模式")
        Com_Key("Fluorescent_Pen")#按下荧光笔组合键

"""红笔"""
def Red_Pen_1shot(hds_lm):
    """
        判断一帧是否符合红笔模式
        :param hds_lm:
        """
    loss = 0  # 和标准红笔手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    #print("current_dist：",current_dist)

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.RED_PEN_GESTURE_STD[i])
    #print("和标准红笔手势的误差为", loss)
    if loss < data.LOSS_RED_PEN:
        return True
    else:
        return False

def Red_Pen_distinguish(hds_lm):
    """
    红笔
    只负责在合理的条件下按下组合键
    :param hds_lm:
    """
    """如果已经进入红笔模式"""
    if data.MOUSE_FLAG[1] >= data.CONTINUE_RED_PEN_THRESHOLD:
        return

    if Red_Pen_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.MOUSE_FLAG[1] += 1
    else:  # 当前帧不是启动手势,启动位归0
        data.MOUSE_FLAG[1] = 0
    if data.MOUSE_FLAG[1] >= data.CONTINUE_RED_PEN_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        #print("进入mouse模式")
        Com_Key("Red_Pen")#按下荧光笔组合键

"""水笔"""
def Clear_ALL_1shot(hds_lm):
    """
        判断一帧是否符合水彩笔模式
        :param hds_lm:
        """
    loss = 0  # 和标准荧光笔手势的误差
    current_dist = tools.get_dist(hds_lm, 0)  # 当前检测到的一只手上所有点到腕部距离
    print("current_dist：",current_dist)
    # print("current_dist长度", len(current_dist))  # 0

    for i in range(21):
        loss = loss + abs(current_dist[i] - data.CLEAR_ALL_STD[i])
    print("和标准水彩笔手势的误差为", loss)
    if loss < data.LOSS_CLEAR_ALL:
        return True
    else:
        return False

def Clear_ALL_distinguish(hds_lm):
    """
    水彩笔
    只负责在合理的条件下按下组合键
    :param hds_lm:
    """
    """如果已经进入清屏模式"""
    if data.MOUSE_FLAG[1] >= data.CONTINUE_CLEAR_ALL_THRESHOLD:
        return

    if Clear_ALL_1shot(hds_lm) and Finger_inBox(hds_lm):  # 当前帧是启动手势,并且食指指尖得在框内才算
        data.MOUSE_FLAG[3] += 1
    else:  # 当前帧不是水彩笔手势,启动位归0
        data.MOUSE_FLAG[3] = 0
    if data.MOUSE_FLAG[3] >= data.CONTINUE_CLEAR_ALL_THRESHOLD:  # 连续启动帧达到20帧
        # 启动
        #print("进入mouse模式")
        Com_Key("Clear_All")#按下清屏组合键

def Mouse(hds_lm):
    """
    鼠标模式判别和执行函数
    :param hds_lm: 手[1][21]
    """
    # 1.1食指向上10帧,并且需要食指指尖在框内
    if data.flag[4] < data.CONTINUE_MOUSE_THRESHOLD:
        mouse_distinguish(hds_lm)
    else:
        """已进入鼠标模式"""

        """功能1：处理鼠标位移"""
        # 1.1:计算当前帧食指尖所在位置,x1 y1是 摄像头坐标系
        x1 = hds_lm[0][8].x * data.wCam
        y1 = hds_lm[0][8].y * data.hCam

        #1.2：x3 y3是 屏幕坐标系"
        x3 = (x1 - data.pt1[0]) * data.wScr / data.wPt
        y3 = (y1 - data.pt1[1]) * data.hScr / data.hPt

        #1.3：摄像头坐标系
        pLocx = data.memory_landmark[8].x * data.wCam
        pLocy = data.memory_landmark[8].y * data.hCam

        #1.4：屏幕坐标系
        pLocx = (pLocx - data.pt1[0]) * data.wScr / data.wPt
        pLocy = (pLocy - data.pt1[1]) * data.hScr / data.hPt

        #1.5：平滑  使手指在移动鼠标时，鼠标箭头不会一直晃动
        cLocx = pLocx + (x3 - pLocx) / data.smooth  # 当前的鼠标所在位置坐标
        cLocy = pLocy + (y3 - pLocy) / data.smooth

        # 1.6：移动鼠标,越界不移动
        if cLocx < data.wScr and cLocx > 0 and cLocy < data.hScr and cLocy > 0:
            print("clotx=", cLocx, "cloty", cLocy)
            autopy.mouse.move(cLocx, cLocy)  # 给出鼠标移动位置坐标

        """功能2：换笔"""

        #2.1:换荧光笔
        Fluorescent_Pen_distinguish(hds_lm)
        # 2.2:换红笔
        Red_Pen_distinguish(hds_lm)
        #2.3:换橡皮
        Rubber_distinguish(hds_lm)
        """根据方案变化"""
        #2.4:换水彩笔
        """只有当前方案有CLEAR才判断"""
        if data.CLEAR_ALL_STD!=[]:
            Clear_ALL_distinguish(hds_lm)



        """功能3：单击"""
        #点击
        if click_1shot(hds_lm):
            if data.CLICK_HOLD_FLAG == 0:
                data.CLICK_HOLD_FLAG = 1
                autopy.mouse.toggle(button=autopy.mouse.Button.LEFT, down=True)  # 按下左键
        #松开
        else:
            if data.CLICK_HOLD_FLAG == 1:
                data.CLICK_HOLD_FLAG = 0
                autopy.mouse.toggle(button=autopy.mouse.Button.LEFT, down=False)  # 松开左键

        """功能4：退出鼠标模式"""

        """若重新检测到手掌启动手势,并且达到一定帧数（和启动思路一样）,则关闭荧光笔模式，清空flag[4]"""
        #退出鼠标模式需要还原的参数
        if start_1shot(hds_lm):  # 当前帧是启动手势
            data.flag[0] += 1
        else:  # 当前帧不是启动手势,启动位归0
            data.flag[0] = 0
        if data.flag[0] >= data.CONTINUE_THRESHOLD:  # 连续启动帧达到20帧
            # 清空鼠标模式的参数
            data.flag[4] = 0#未进入鼠标模式
            data.CLICK_HOLD_FLAG = 0
            data.FIRST_MOUSE = 1
            data.MOUSE_FLAG = [0, 0, 0,0,0]#鼠标记录清零
            Com_Key("mouse")  # 变成鼠标
